package concurrency.ping_pong.moniteur_lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MoniteurPingPong {

	// ====== VARIABLES D'ETATS ======
	private boolean tour;
	// ===============================
	private int repetition;

	private Lock verrou = new ReentrantLock();
	// ====== VARIABLES CONDITIONS ======
	private Condition condPing = verrou.newCondition();
	private Condition condPong = verrou.newCondition();
	// ==================================

	public MoniteurPingPong(int repetition) {
		// ====== INITIALISATION ======
		tour = false;
		// ============================
		this.repetition = repetition;
	}

	// ====== POINT ENTREE MONITEUR ======
	private void ecrirePing() throws InterruptedException {
		verrou.lock();
		try {
			while (tour) {
				condPing.await();
			}
			System.out.print("Ping-");
			tour = true;
			condPong.signal();
		} finally {
			verrou.unlock(); // IMPORTANT... ne pas oublier de relacher le verrou !!
		}
	}

	// ====== POINT ENTREE MONITEUR ======
	private void ecrirePong() throws InterruptedException {
		verrou.lock();
		try {
			while (!tour) {
				condPong.await();
			}
			tour = false;
			System.out.println("Pong");
			condPing.signal();
		} finally {
			verrou.unlock();
		}
	}

	// ========== FIN MONITEUR ==========   

	// ========== PROCESSUS ========== 
	private class Ping extends Thread {
		@Override
		public void run() {
			try {
				// ========
				for (int i = 0; i < repetition; i++) {
					ecrirePing();
				}
				// ========
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	private class Pong extends Thread {
		@Override
		public void run() {
			try {
				// ========
				for (int i = 0; i < repetition; i++) {
					ecrirePong();
				}
				// ========
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) {
		MoniteurPingPong moniteur = new MoniteurPingPong(2000);
		Pong pong = moniteur.new Pong();
		Ping ping = moniteur.new Ping();
		pong.start();
		ping.start();
	}
}
